#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMouseEvent>
#include <QMessageBox>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    for(int i = 0;i!=this->widthNum*this->heightNum;++i)
        this->theBarrier.push_back(BarrierBlock());
    setMyShape();
    this->setWindowOpacity(0.9);
    this->setFixedSize(QSize(600, 773));
    this->setWindowFlags(Qt::FramelessWindowHint);
    Connect();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::Connect()
{
    connect(ui->btn_start,&QPushButton::clicked,[=]()
    {
        if(this->gamePixmap)
            return;
        this->createGamePixmap();
        this->timer->start(this->mse);
        getRandShape();
    });

    connect(this->timer,&QTimer::timeout,[=]()
    {
        this->runTime+=this->mse/1000.0;
        ui->lab_Time->setText(QString::number(this->runTime,'f',1)+"(s)");
        if(this->cannotDown())
            this->addcurShapeToBarrier();
        else
            this->curShape->downStep();
        DealfullLine();
        drawCurShape();
        if(isFail())
        {
            QMessageBox::information(this,"ATTENTION","GAME OVER-YOUR SCORE IS "+QString::number(this->score),QMessageBox::Ok);
            ui->btn_quit->click();
        }
    });

    connect(ui->btn_quit,&QPushButton::clicked,[=]()
    {
       if(this->gamePixmap)
       {
           this->timer->stop();
           delete this->gamePixmap;
           this->gamePixmap = nullptr;
           ui->gameLab->clear();
           this->runTime=0.0;
           ui->lab_Time->setText(QString::number(this->runTime,'f',1)+"(s)");
           this->score = 0;
           ui->lab_score->setText(QString::number(this->score));
           if(this->curShape)
           {
               delete this->curShape;
               this->curShape = nullptr;
           }
           this->theBarrier.clear();
           for(int i = 0;i!=this->widthNum*this->heightNum;++i)
               this->theBarrier.push_back(BarrierBlock());
       }
    });

    connect(ui->btn_close,&QPushButton::clicked,[=]()
    {
       this->close();
    });

    connect(ui->btn_winMin,&QPushButton::clicked,[=]()
    {
       this->showMinimized();
    });
}

void Widget::createGamePixmap()
{
    this->gamePixmap = new QPixmap(this->widthNum*this->blockSize.width()+1,this->heightNum*this->blockSize.height()+1);
    this->gamePixmap->fill();
    QPainter *painter = new QPainter(this->gamePixmap);
    painter->setPen(QColor(0,0,0));
    for(int i = 0;i!=this->widthNum+1;++i)
    {
        painter->drawLine(QLine(QPoint(i*this->blockSize.width(),0),QPoint(i*this->blockSize.width(),this->blockSize.height()*this->heightNum)));
    }
    for(int i = 0;i!=this->heightNum+1;++i)
    {
        painter->drawLine(QLine(QPoint(0,i*this->blockSize.height()),QPoint(this->widthNum*this->blockSize.width(),i*this->blockSize.height())));
    }
    ui->gameLab->setPixmap(*this->gamePixmap);
    delete painter;
}

void Widget::mouseMoveEvent(QMouseEvent *Event)
{
    if(!this->isWinMove)
    {
        this->curMousePos = Event->globalPosition();
        this->isWinMove = true;
        return;
    }
    auto temp = Event->globalPosition()-this->curMousePos;
    this->move(this->pos().x()+static_cast<int>(temp.x()),this->pos().y()+static_cast<int>(temp.y()));
    this->curMousePos = Event->globalPosition();
}

void Widget::mouseReleaseEvent(QMouseEvent *)
{
    this->isWinMove = false;
}

void Widget::keyPressEvent(QKeyEvent *event)
{
    if(event->key()==Qt::Key_W&&this->gamePixmap!=nullptr)
    {
        ui->btn_W->setStyleSheet("selection-background-color: darkgray;"+
                                   QString("border: 5px solid rgb(99, 99, 99);")+
                                   QString("color: rgb(99, 99, 99);")+
                                   QString("background-color: rgb(209, 77, 73);"));
        this->curShape->turnStep();
        if(isInBerrier()||this->curShape->getUpOrder().y()<0)
        {
            this->curShape->turnStep();
            this->curShape->turnStep();
            this->curShape->turnStep();
            return;
        }
        while(this->curShape->getLeftOrder().x()<0)
            this->curShape->rightStep();
        while(this->curShape->getRightOrder().x()>this->widthNum-1)
            this->curShape->leftStep();
        drawCurShape();
    }
    if(event->key()==Qt::Key_A&&this->gamePixmap!=nullptr)
    {
        ui->btn_A->setStyleSheet("selection-background-color: darkgray;"+
                                   QString("border: 5px solid rgb(99, 99, 99);")+
                                   QString("color: rgb(99, 99, 99);")+
                                   QString("background-color: rgb(209, 77, 73);"));
        if(this->curShape->getLeftOrder().x()==0||this->cannotLeft())
            return;
        //true代表已然成为障碍
        this->curShape->leftStep();
        DealfullLine();
        drawCurShape();
        if(isFail())
        {
            this->timer->stop();
            QMessageBox::information(this,"ATTENTION","GAME OVER-YOUR SCORE IS "+QString::number(this->score),QMessageBox::Ok);
            ui->btn_quit->click();
        }
    }
    if(event->key()==Qt::Key_S&&this->gamePixmap!=nullptr)
    {
        ui->btn_S->setStyleSheet("selection-background-color: darkgray;"+
                                   QString("border: 5px solid rgb(99, 99, 99);")+
                                   QString("color: rgb(99, 99, 99);")+
                                   QString("background-color: rgb(209, 77, 73);"));
        if(this->cannotDown())
            this->addcurShapeToBarrier();
        else
            this->curShape->downStep();
        DealfullLine();
        drawCurShape();
        if(isFail())
        {
            this->timer->stop();
            QMessageBox::information(this,"ATTENTION","GAME OVER-YOUR SCORE IS "+QString::number(this->score),QMessageBox::Ok);
            ui->btn_quit->click();
        }
    }
    if(event->key()==Qt::Key_D&&this->gamePixmap!=nullptr)
    {
        ui->btn_D->setStyleSheet("selection-background-color: darkgray;"+
                                   QString("border: 5px solid rgb(99, 99, 99);")+
                                   QString("color: rgb(99, 99, 99);")+
                                   QString("background-color: rgb(209, 77, 73);"));
        if(this->curShape->getRightOrder().x()==this->widthNum-1||this->cannotRight())
            return;
        this->curShape->rightStep();
        DealfullLine();
        drawCurShape();
        if(isFail())
        {
            this->timer->stop();
            QMessageBox::information(this,"ATTENTION","GAME OVER-YOUR SCORE IS "+QString::number(this->score),QMessageBox::Ok);
            ui->btn_quit->click();
        }
    }
}

void Widget::keyReleaseEvent(QKeyEvent *event)
{
    if(event->key()==Qt::Key_W&&this->gamePixmap!=nullptr)
    {
        ui->btn_W->setStyleSheet("selection-background-color: darkgray;"+
                                   QString("border: 5px solid rgb(99, 99, 99);")+
                                   QString("color: rgb(99, 99, 99);"));
    }
    if(event->key()==Qt::Key_A&&this->gamePixmap!=nullptr)
    {
        ui->btn_A->setStyleSheet("selection-background-color: darkgray;"+
                                   QString("border: 5px solid rgb(99, 99, 99);")+
                                   QString("color: rgb(99, 99, 99);"));
    }
    if(event->key()==Qt::Key_S&&this->gamePixmap!=nullptr)
    {
        ui->btn_S->setStyleSheet("selection-background-color: darkgray;"+
                                   QString("border: 5px solid rgb(99, 99, 99);")+
                                   QString("color: rgb(99, 99, 99);"));
    }
    if(event->key()==Qt::Key_D&&this->gamePixmap!=nullptr)
    {
        ui->btn_D->setStyleSheet("selection-background-color: darkgray;"+
                                   QString("border: 5px solid rgb(99, 99, 99);")+
                                   QString("color: rgb(99, 99, 99);"));
    }
}

void Widget::setMyShape()
{
    vector<vector<QPoint>> temp;
    vector<QPoint> temp2;
    //shape1
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(1,0));
    temp2.push_back(QPoint(0,-1));
    temp.push_back(temp2);
    temp.push_back(temp2);
    temp.push_back(temp2);
    temp.push_back(temp2);
    Shape temp3(QPoint(0,0),temp);
    this->myShape.push_back(temp3);
    temp2.clear();
    temp.clear();
    //shape2
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp.push_back(temp2);
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(0,2));
    temp2.push_back(QPoint(-1,0));
    temp2.push_back(QPoint(-1,0));
    temp2.push_back(QPoint(3,0));
    temp.insert(temp.begin()+1,temp2);
    temp.push_back(temp2);
    Shape temp4(QPoint(0,0),temp);
    this->myShape.push_back(temp4);
    temp2.clear();
    temp.clear();
    //shape3
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(-1,0));
    temp2.push_back(QPoint(2,0));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(-1,0));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,-2));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(1,0));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,-2));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(-1,-1));
    temp2.push_back(QPoint(2,0));
    temp.push_back(temp2);
    temp2.clear();
    Shape temp5(QPoint(0,0),temp);
    this->myShape.push_back(temp5);
    temp.clear();
    //shape4
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(-1,0));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(-1,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(1,0));
    temp2.push_back(QPoint(1,0));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(1,0));
    temp2.push_back(QPoint(-1,1));
    temp2.push_back(QPoint(0,1));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(-1,0));
    temp2.push_back(QPoint(2,0));
    temp2.push_back(QPoint(0,1));
    temp.push_back(temp2);
    temp2.clear();
    Shape temp6(QPoint(0,0),temp);
    this->myShape.push_back(temp6);
    temp.clear();
    //shape5
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(-1,0));
    temp2.push_back(QPoint(0,1));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(-1,0));
    temp2.push_back(QPoint(1,1));
    temp2.push_back(QPoint(1,0));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(-1,1));
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(1,0));
    temp2.push_back(QPoint(0,1));
    temp.push_back(temp2);
    temp2.clear();
    temp2.push_back(QPoint(0,1));
    temp2.push_back(QPoint(1,0));
    temp2.push_back(QPoint(-1,1));
    temp2.push_back(QPoint(-1,0));
    temp.push_back(temp2);
    temp2.clear();
    Shape temp7(QPoint(0,0),temp);
    this->myShape.push_back(temp7);
    temp.clear();
}

void Widget::getRandShape()
{
    if(this->curShape)
    {
        delete curShape;
        curShape = nullptr;
    }
    int order = rand()%5;
    switch(order)
    {
    case 0:
    {
        this->blockBrush = QBrush(QColor(66, 206, 201));
        this->curShape = new Shape(*this->myShape.begin());
        this->curShape->setHead(QPoint(7,-4));
    }break;
    case 1:
    {
        this->blockBrush = QBrush(QColor(240, 152, 65));
        this->curShape = new Shape(*(this->myShape.begin()+1));
        this->curShape->setHead(QPoint(7,-4));
    }break;
    case 2:
    {
        this->blockBrush = QBrush(QColor(186, 84, 58));
        this->curShape = new Shape(*(this->myShape.begin()+2));
        this->curShape->setHead(QPoint(7,-4));
    }break;
    case 3:
    {
        this->blockBrush = QBrush(QColor(75, 148, 70));
        this->curShape = new Shape(*(this->myShape.begin()+3));
        this->curShape->setHead(QPoint(7,-4));
    }break;
    case 4:
    {
        this->blockBrush = QBrush(QColor(140, 150, 217));
        this->curShape = new Shape(*(this->myShape.begin()+4));
        this->curShape->setHead(QPoint(7,-4));
    }break;
    default:
    {
        this->blockBrush = QBrush(QColor(66, 206, 201));
        this->curShape = new Shape(*this->myShape.begin());
        this->curShape->setHead(QPoint(7,-4));
    }
    }
}

void Widget::drawCurShape()
{
    QPixmap pixmap = *this->gamePixmap;
    QPoint p = this->curShape->getHead();
    for(const auto &elem:(curShape->getCurIter()))
    {
        p+=elem;
        if(p.x()>=0&&p.x()<this->widthNum&&p.y()>=0&&p.y()<this->heightNum)
            drawBlock(pixmap,p);
    }
    for(int i = 0;i<this->heightNum;++i)
    {
        for(int j = 0;j<this->widthNum;++j)
        {
            if(isBarrier(QPoint(j,i)))
                drawBlock(pixmap,QPoint(j,i),false);
        }
    }
    ui->gameLab->setPixmap(pixmap);
}

bool Widget::cannotDown()
{
    //判段是否底部接触
    //如果接触，将此次的curShape所指的几个block纳入到已完成的shapeBlock里
    //然后更新curshap
    for(const auto &elem: this->curShape->getDownOrderVector())
    {
        QPoint temp = (elem+QPoint(0,1));
        if(temp.x()>=0&&temp.x()<this->widthNum&&temp.y()>=0&&temp.y()<this->heightNum&&isBarrier(temp))
            return true;
        if(elem.y()==this->heightNum-1)
            return true;
    }
    return false;
}

void Widget::addcurShapeToBarrier()
{
    QPoint p = this->curShape->getHead();
    for(const auto &elem2:(curShape->getCurIter()))
    {
        p+=elem2;
        if(p.x()>=0&&p.x()<this->widthNum&&p.y()>=0&&p.y()<this->heightNum)
        {
            this->recurBlock(p.x(),p.y()).isBarrier=true;
            this->recurBlock(p.x(),p.y()).brush = QBrush(this->blockBrush.color());
        }
    }
    this->getRandShape();
}

bool Widget::cannotLeft()
{
    for(const auto &elem: this->curShape->getLeftOrderVector())
    {
        QPoint temp = (elem+QPoint(-1,0));
        if(temp.x()>=0&&temp.x()<this->widthNum&&temp.y()>=0&&temp.y()<this->heightNum&&isBarrier(temp))
            return true;
    }
    return false;
}

bool Widget::cannotRight()
{
    for(const auto &elem: this->curShape->getRightOrderVector())
    {
        QPoint temp = (elem+QPoint(1,0));
        if(temp.x()>=0&&temp.x()<this->widthNum&&temp.y()>=0&&temp.y()<this->heightNum&&isBarrier(temp))
            return true;
    }
    return false;
}

void Widget::DealfullLine()
{
    for(int i = this->heightNum-1;i>=0;--i)
    {
        int j = 0;
        for(;j<this->widthNum;++j)
        {
            if(recurBlock(j,i).isBarrier==false)
                break;
        }
        if(j==this->widthNum)
        {
            this->score +=this->widthNum;
            ui->lab_score->setText(QString::number(this->score));
            for(int k = i;k>0;--k)
            {
                for(int l = 0;l<this->widthNum;++l)
                {
                    recurBlock(l,k).brush=recurBlock(l,k-1).brush;
                    recurBlock(l,k).isBarrier=recurBlock(l,k-1).isBarrier;
                    recurBlock(l,k-1).isBarrier=false;
                }

            }
            ++i;
        }
    }
}

bool Widget::isFail()
{
    for(int j = 0;j!=this->widthNum;++j)
    {
        if(isBarrier(QPoint(j,0)))
                return true;
    }
    return false;
}

bool Widget::isInBerrier()
{
    auto temp = this->curShape->getHead();
    for(const auto &elem:this->curShape->getCurIter())
    {
        temp+=elem;
        if(temp.y()>=this->heightNum||temp.y()<0)
            return true;
        if(isBarrier(temp))
            return true;

    }
    return false;
}

void Widget::drawBlock(QPixmap &pixmap, const QPoint &p, bool isShapeStyle)
{
    QPainter *painter =new QPainter(&pixmap);
    painter->fillRect(p.x()*this->blockSize.width()+1,p.y()*this->blockSize.height()+1,
                      this->blockSize.width()-1,this->blockSize.height()-1,isShapeStyle?this->blockBrush:recurBlock(p.x(), p.y()).brush);
    delete painter;
}

bool Widget::isBarrier(const QPoint &p)
{
    return recurBlock(p.x(),p.y()).isBarrier;
}

Widget::BarrierBlock &Widget::recurBlock(int x, int y)
{
    return this->theBarrier.at(static_cast<vector<BarrierBlock>::size_type>(y*this->widthNum+x));
}
